package com.bl.student.dao.redis;

import com.bl.student.common.utils.CamelUtil;
import com.bl.student.common.utils.ReflectionUtil;
import com.bl.student.dao.redis.client.RedisService;
import org.redisson.api.*;
import org.redisson.client.protocol.ScoredEntry;
import org.springframework.beans.factory.annotation.Autowired;

import javax.annotation.PreDestroy;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * Created by yujingyi on 2017/5/12.
 */
public abstract class AbstractDao {

    private static RedissonClient redisson = null;

    @Autowired
    RedisService redisService;
    //
//    protected static RedissonClient redisson;
//
    protected RedissonClient getRedisson() {
        if (redisson == null || redisson.isShutdown() || redisson.isShuttingDown()) {
            redisson = redisService.getInstance();
        }
        return redisson;
    }

    @PreDestroy
    public void shutdown() {
        if (redisson != null && !redisson.isShutdown() && !redisson.isShuttingDown()) {
            System.out.println("shutdown redisson...");
            redisson.shutdown();
        }
    }
//
//    public void init() {
//        System.out.println(redisService);
////        redisson = redisService.getInstance();
//    }
//
//    public AbstractDao() {
//        init();
//    }

    protected String genKey(String prefix, String key) {
        StringBuilder builder = new StringBuilder()
                .append(prefix)
                .append(key);

        return builder.toString();
    }

    protected String genKey(String prefix, String account, String key) {
        StringBuilder builder = new StringBuilder()
                .append(prefix)
                .append(account)
                .append("_")
                .append(key);

        return builder.toString();
    }

    protected String genKey(String prefix, String account, String key, String extra) {
        StringBuilder builder = new StringBuilder()
                .append(prefix)
                .append(account)
                .append("_")
                .append(key)
                .append("_")
                .append(extra);

        return builder.toString();
    }

    protected String get(String key) {
        RedissonClient redisson = getRedisson();
        RBucket<String> rBucket = redisson.getBucket(key);
        return rBucket.get();
    }

    protected void set(String key, String val) {
        set(key, val, 0);
    }

    protected void set(String key, String val, long expire) {
        RedissonClient redisson = getRedisson();
        RBucket<String> rBucket = redisson.getBucket(key);
        if (expire > 0) {
            rBucket.set(val, expire, TimeUnit.MILLISECONDS);
        } else {
            rBucket.set(val);
        }
    }

    protected long del(String key) {
        RedissonClient redisson = getRedisson();
        RKeys rKeys = redisson.getKeys();
        return rKeys.delete(key);
    }

    protected void expire(String key, long expire) {
        RedissonClient redisson = getRedisson();
        RKeys rKeys = redisson.getKeys();
        rKeys.expire(key, expire, TimeUnit.SECONDS);
    }

    protected void pexpire(String key, long expire) {
        RedissonClient redisson = getRedisson();
        RKeys rKeys = redisson.getKeys();
        rKeys.expire(key, expire, TimeUnit.MILLISECONDS);
    }

    protected Long getLong(String key) {
        RedissonClient redisson = getRedisson();
        RAtomicLong rAtomicLong = redisson.getAtomicLong(key);
        return rAtomicLong.get();
    }

    protected Long incr(String key, int expire) {
        RedissonClient redisson = getRedisson();
        RAtomicLong rAtomicLong = redisson.getAtomicLong(key);
        Long value = rAtomicLong.incrementAndGet();
        if (expire > 0) {
            rAtomicLong.expire(expire, TimeUnit.MILLISECONDS);
        }
        return value;
    }

    protected Long decr(String key, int expire) {
        RedissonClient redisson = getRedisson();
        RAtomicLong rAtomicLong = redisson.getAtomicLong(key);
        Long value = rAtomicLong.decrementAndGet();
        if (expire > 0) {
            rAtomicLong.expire(expire, TimeUnit.MILLISECONDS);
        }
        return value;
    }

    protected void hsetAll(String key, Object object) {
        RedissonClient redisson = getRedisson();
        RMap<String, Object> rMap = redisson.getMap(key);

        Map<String, Object> map = ReflectionUtil.objectToMap(object, false, true);
        rMap.putAll(map);
    }

    protected void hset(String key, String attr, String value) {
        RedissonClient redisson = getRedisson();
        RMap<String, Object> rMap = redisson.getMap(key);

        rMap.fastPut(CamelUtil.camelToUnderScore(attr), value);
    }

    protected <T> T hgetAll(String key, Class<T> clazz) {
        RedissonClient redisson = getRedisson();

        RMap<String, Object> rMap = redisson.getMap(key);

        return convertMapToVo(rMap.readAllMap(), clazz);
    }

    protected long hdel(String key, String val) {
        RedissonClient redisson = getRedisson();
        RMap<String, Object> rMap = redisson.getMap(key);
        return rMap.fastRemove(val);
    }

    protected List<String> lgetAll(String key) {
        RedissonClient redisson = getRedisson();

        RList<String> rList = redisson.getList(key);
        return rList.readAll();
    }

    protected void lpush(String key, String newVal) {
        RedissonClient redisson = getRedisson();

        RDeque<String> rDeque = redisson.getDeque(key);
        rDeque.addFirst(newVal);
    }

    protected boolean lrem(String key, String val) {
        RedissonClient redisson = getRedisson();
        RDeque<String> rDeque = redisson.getDeque(key);
        return rDeque.removeFirstOccurrence(val);
    }

    protected void lrem(String key, int index) {
        RedissonClient redisson = getRedisson();
        RList<String> rList = redisson.getList(key);
        rList.fastRemove(index);
    }

    protected boolean zadd(String key, String val, double score) {
        RedissonClient redisson = getRedisson();
        RScoredSortedSet scoredSortedSet = redisson.getScoredSortedSet(key);
        return scoredSortedSet.add(score, val);
    }

    protected Collection<String> zrange(String key, double start, double end) {
        RedissonClient redisson = getRedisson();
        RScoredSortedSet scoredSortedSet = redisson.getScoredSortedSet(key);
        return scoredSortedSet.valueRange(start, true, end, true);
    }

    protected Collection<ScoredEntry<String>> zrangewithscores(String key, double start, double end) {
        RedissonClient redisson = getRedisson();
        RScoredSortedSet scoredSortedSet = redisson.getScoredSortedSet(key);
        return scoredSortedSet.entryRange(start, true, end, true);
    }

    protected boolean zrem(String key, String val) {
        RedissonClient redisson = getRedisson();
        RScoredSortedSet scoredSortedSet = redisson.getScoredSortedSet(key);
        return scoredSortedSet.remove(val);
    }

    protected int zremrangebyscore(String key, double start, double end) {
        RedissonClient redisson = getRedisson();
        RScoredSortedSet scoredSortedSet = redisson.getScoredSortedSet(key);
        return scoredSortedSet.removeRangeByScore(start, true, end, true);
    }

    protected long pttl(String key) {
        RedissonClient redisson = getRedisson();
        RPermitExpirableSemaphore rPermitExpirableSemaphore = redisson.getPermitExpirableSemaphore(key);
        return rPermitExpirableSemaphore.remainTimeToLive();
    }

    protected <T> T convertMapToVo(Map<String, Object> map, Class<T> clazz) {
        if (map == null || map.isEmpty()) {
            return null;
        }

        T t = null;
        try {
            t = clazz.newInstance();
            Set<Field> fields = ReflectionUtil.getClassAllFields(clazz);
            for (Field field : fields) {
                String key = CamelUtil.camelToUnderScore(field.getName());
                Object value = map.get(key);
                if (value != null) {
                    field.setAccessible(true);
                    field.set(t, value);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return t;
    }



}
